package com.rf.richfitwheel.common.utils;

import com.jcraft.jsch.ChannelSftp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * zip 文件压缩
 * @author
 * @date 2023/09/06
 */
@Slf4j
public class ZipUtil {

    /**
     * 压缩指定文件  返回输出流
     * @param fileNames 文件名
     * @param response
     * @throws Exception
     */
    public static void zipFile(List<String> fileNames, HttpServletResponse response) {
        ZipOutputStream zipOutputStream = null;
        FileInputStream fileInputStream = null;
        try {
            // 设置响应信息
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + java.net.URLEncoder.encode(UuidUtil.get32UUID() + ".zip", "UTF-8"));
            response.addHeader("Pargam", "no-cache");
            response.addHeader("Cache-Control", "no-cache");
            // 返回输出流
            zipOutputStream = new ZipOutputStream(response.getOutputStream());
            for (String fileName : fileNames) {
                File file = new File(fileName);
                // 处理截断父目录
                String parentDir = parentDir(file, fileName.substring(0, fileName.lastIndexOf("/")));
                // 文件输入流
                fileInputStream = new FileInputStream(file);
                // 创建zip实体 并添加进压缩包
                zipOutputStream.putNextEntry(new ZipEntry(FilenameUtils.separatorsToUnix(parentDir) + file.getName()));
                byte[] bytes = new byte[fileInputStream.available()];
                // 读取文件
                fileInputStream.read(bytes);
                // 写入输出流
                zipOutputStream.write(bytes);
                zipOutputStream.closeEntry();
            }
            // 刷新
            zipOutputStream.flush();
        } catch (IOException e) {
            log.error("压缩异常",e.getMessage());
        } finally {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.close();
                }
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                // 删除本地下载的文件
                fileNames.stream().forEach(fileName -> new File(fileName).delete());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 压缩文件流数据  返回输出流
     * @param files 文件流数据
     * @throws Exception
     */
    public static byte[] zipStream(List<ByteArrayOutputStream> files) {
        ZipOutputStream zipOutputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            // 返回输出流
            zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
            for (ByteArrayOutputStream outputStream : files) {
                // 创建zip实体 并添加进压缩包
                ZipEntry zipEntry = new ZipEntry(UuidUtil.get16NumUUID() + ".pdf");
                zipEntry.setSize(outputStream.toByteArray().length);
                zipOutputStream.putNextEntry(zipEntry);
                // 写数据
                zipOutputStream.write(outputStream.toByteArray());
                zipOutputStream.closeEntry();
            }
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            log.error("压缩异常", e.getMessage());
        } finally {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                log.error("流关闭异常", e.getMessage());
            }
        }
        return null;
    }
    public static byte[] zipStreamForMap(List<Map<String, Object>> files) {
        ZipOutputStream zipOutputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            // 返回输出流
            zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
            for (Map<String, Object> map : files) {
                ByteArrayOutputStream outputStream = (ByteArrayOutputStream)map.get("stream");;
                // 创建zip实体 并添加进压缩包
                ZipEntry zipEntry = new ZipEntry(UuidUtil.get16NumUUID() + "." + map.get("fileType"));
                zipEntry.setSize(outputStream.toByteArray().length);
                zipOutputStream.putNextEntry(zipEntry);
                // 写数据
                zipOutputStream.write(outputStream.toByteArray());
                zipOutputStream.closeEntry();
            }
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            log.error("压缩异常", e.getMessage());
        } finally {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                log.error("流关闭异常", e.getMessage());
            }
        }
        return null;
    }

    /**
     * 将zip文件存储在本地 操作完之后删除
     * @param files
     * @param zipFileName
     */
    public static void zipStream(List<ByteArrayOutputStream> files, String zipFileName) {
        ZipOutputStream zipOutputStream = null;
        try {
            createParent(zipFileName);
            // 返回输出流
            zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileName));
            for (ByteArrayOutputStream outputStream : files) {
                // 创建zip实体 并添加进压缩包
                ZipEntry zipEntry = new ZipEntry(UuidUtil.get16NumUUID() + ".pdf");
                zipEntry.setSize(outputStream.toByteArray().length);
                zipOutputStream.putNextEntry(zipEntry);
                // 写数据
                zipOutputStream.write(outputStream.toByteArray());
                zipOutputStream.closeEntry();
                outputStream.close();
            }
            zipOutputStream.flush();
        } catch (IOException e) {
            log.error("压缩异常", e.getMessage());
        } finally {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.close();
                }
            } catch (IOException e) {
                log.error("流关闭异常", e.getMessage());
            }
        }
    }


    /**
     * @param urls
     * @param zipFileName
     */
    public static void downloadAndZip(List<String> urls, String zipFileName, String sftpIp, Integer sftpPort, String sftpUserName, String sftpPassWord) {
        ZipOutputStream zipOutputStream = null;
        try {
            ChannelSftp channelSftp = SftpUtil.getSftpChannel(sftpIp, sftpPort, sftpUserName, sftpPassWord);
            createParent(zipFileName);
            // 返回输出流
            zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileName));
            for (String url : urls) {
                ByteArrayOutputStream fileOutputStreams = SftpUtil.download(channelSftp, url);
                // 创建zip实体 并添加进压缩包
                ZipEntry zipEntry = new ZipEntry(url.substring(url.lastIndexOf("/")+1, url.length()));
                zipEntry.setSize(fileOutputStreams.toByteArray().length);
                zipOutputStream.putNextEntry(zipEntry);
                // 写数据
                zipOutputStream.write(fileOutputStreams.toByteArray());
                zipOutputStream.closeEntry();
                fileOutputStreams.close();
            }
            // 关闭链接
            SftpUtil.closeSession(channelSftp);
            zipOutputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("压缩异常", e.getMessage());
        } finally {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.close();
                }
            } catch (IOException e) {
                log.error("流关闭异常", e.getMessage());
            }
        }
    }

    /**
     * 创建文件的目录结构
     * @param zipFileName
     * @throws IOException
     */
    private static void createParent(String zipFileName) throws IOException {
        File file = new File(zipFileName);
        if (!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
    }

    /**
     * 处理截断父目录，判断截断父目录是否存在，并规范路径格式，以“/”或“\”分隔符结尾
     * @param sourceFile 压缩文件
     * @param parentSub  需截断目录
     * @return 截断后目录结构
     */
    private static String parentDir(File sourceFile, String parentSub) {
        // 处理截断父目录，判断截断父目录是否存在，并规范路径格式，以“/”或“\”分隔符结尾
        String parentDir = "";
        if (StringUtils.isNotEmpty(parentSub)) {
            String parentPath = sourceFile.getParent() + File.separator;
            File parentSubFile = new File(parentSub);
            if (parentSubFile.exists() && parentSubFile.isDirectory()) {
                String psub = parentSubFile.getAbsolutePath() + File.separator;
                if (parentPath.startsWith(psub)) {
                    parentDir = parentPath.substring(psub.length());
                }
            }
        }

        return parentDir;
    }


    public static void main(String[] args) throws Exception {
        /** 测试 */
        String outPath = "D:\\compress.zip";
        ZipOutputStream zipOutputStream = null;
        FileInputStream fileInputStream = null;
        List<String> fileNames = new ArrayList<String>(){{
            add("D:\\B202307011800412.pdf");
            add("D:\\B20230701050011.pdf");
        }};
        String parentSub = "D:\\";

        try {
            // 返回输出流
            zipOutputStream = new ZipOutputStream(new FileOutputStream(outPath));
            for (String fileName : fileNames) {
                File file = new File(fileName);

                // 处理截断父目录
                String parentDir = parentDir(file, parentSub);

                // 文件输入流
                fileInputStream = new FileInputStream(file);
                // 创建zip实体 并添加进压缩包
                zipOutputStream.putNextEntry(new ZipEntry(FilenameUtils.separatorsToUnix(parentDir) + file.getName()));
                byte[] bytes = new byte[fileInputStream.available()];
                // 读取文件
                fileInputStream.read(bytes);
                // 写入输出流
                zipOutputStream.write(bytes);
            }
            zipOutputStream.closeEntry();
            // 刷新
            zipOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.close();
                }
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}


